package cc.mllt.demoaopmybatisauth.demos.web.interceptor;

import cc.mllt.demoaopmybatisauth.demos.web.model.entity.DataPermissionRule;
import cc.mllt.demoaopmybatisauth.demos.web.service.CacheDataService;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据权限控制拦截器，基于MyBatis-Plus的InnerInterceptor实现
 * 动态解析SQL并追加数据范围限制条件
 */
public class DataScopeInnerInterceptor implements InnerInterceptor {
    private static final Logger log = LoggerFactory.getLogger(DataScopeInnerInterceptor.class);
    private final CacheDataService cacheDataService;

    public DataScopeInnerInterceptor(CacheDataService cacheDataService) {
        this.cacheDataService = cacheDataService;
    }

    /**
     * 查询前拦截处理（核心方法）
     * 1. 解析原始SQL并提取表名
     * 2. 过滤系统表
     * 3. 获取用户数据权限规则
     * 4. 构造并追加WHERE条件
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
                            RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        try {
            String originalSql = boundSql.getSql();
            log.info("拦截SQL: {}", originalSql);
            if (!StringUtils.hasText(originalSql)) {
                log.warn("SQL为空，跳过数据范围限制处理");
                return;
            }

            List<String> tableNames = extractTableNames(originalSql);
            log.debug("解析表名: {}", tableNames);
            filterOutSpecificTables(tableNames);
            if (tableNames.isEmpty()) return;

            String uid = "1"; // TODO: 替换为实际用户ID获取逻辑

            Map<String, DataPermissionRule> rules = cacheDataService.getDataScopeRules(uid);
            if (rules == null || rules.isEmpty()) {
                log.info("用户[{}]无数据权限规则", uid);
                return;
            }

            Map<String, String> tableRules = getModuleRulesFromTableNames(tableNames, rules, uid);
            if (tableRules.isEmpty()) {
                log.warn("未找到适用的数据范围规则");
                return;
            }

            String modifiedSql = appendMultiDataScopeRules(originalSql, tableRules);
            updateBoundSql(ms, boundSql, modifiedSql);

            log.debug("原始SQL: {}", originalSql);
            log.debug("修改后SQL: {}", modifiedSql);
        } catch (Exception e) {
            log.error("数据权限拦截器异常: {}", e.getMessage(), e);
        }
    }

    /**
     * 过滤系统表（sys/act/data前缀表）
     * @param tableNames 待过滤表名列表（直接修改原集合）
     */
    private void filterOutSpecificTables(List<String> tableNames) {
        var it = tableNames.iterator(); //  terator<String> it = tableNames.iterator();
        while (it.hasNext()) {
            String name = it.next().toLowerCase();
            if (name.startsWith("sys") || name.startsWith("act") || name.startsWith("data")) {
                it.remove();
            }
        }
    }

    /**
     * 提取SQL语句中的表名
     * @param sql 原始SQL语句
     * @return 表名列表（可能包含别名）
     * @throws JSQLParserException SQL解析异常
     */
    private List<String> extractTableNames(String sql) throws JSQLParserException {
        Statement stmt = CCJSqlParserUtil.parse(sql);
        TablesNamesFinder finder = new TablesNamesFinder();

        if (stmt instanceof Select) {
            return finder.getTableList(stmt);
        } else if (stmt instanceof Insert) {
            return List.of(((Insert) stmt).getTable().getName());
        } else if (stmt instanceof Update) {
            return List.of(((Update) stmt).getTable().getName());
        } else if (stmt instanceof Delete) {
            return List.of(((Delete) stmt).getTable().getName());
        }

        throw new UnsupportedOperationException("不支持的SQL类型: " + stmt.getClass());
    }

    /**
     * 根据表名获取对应模块规则
     * @param tableNames 表名列表
     * @param dataScopeRules 数据权限规则集
     * @param uid 用户ID
     * @return 表名与规则条件的映射表
     */
    private Map<String, String> getModuleRulesFromTableNames(List<String> tableNames,
                                                             Map<String, DataPermissionRule> dataScopeRules,
                                                             String uid) {
        Map<String, String> result = new HashMap<>();
        for (String table : tableNames) {
            String module = getModuleNameFromTableName(table);
            DataPermissionRule rule = dataScopeRules.get(module.toLowerCase());
            if (rule != null) {
                String condition = buildDataScopeCondition(rule, uid, table);
                if (StringUtils.hasText(condition)) {
                    result.put(table, condition);
                }
            }
        }
        return result;
    }

    /**
     * 表名到模块名的转换（示例实现）
     * @param tableName 数据库表名
     * @return 对应的模块名
     */
    private String getModuleNameFromTableName(String tableName) {
        // TODO: 根据实际业务逻辑实现表名到模块名的映射
        String result="";
        result=  switch (tableName.toLowerCase()) {
            case "user" -> "user";
            case "data" -> "data";
            default -> "";
        };
        return result.toLowerCase();
    }

    /**
     * 构建带表别名的数据权限条件
     * @param rule 数据权限规则
     * @param uid 用户ID
     * @param tableName 数据库表名
     * @return 条件表达式（如：t_user.create_by = '1'）
     */
    private String buildDataScopeCondition(DataPermissionRule rule, String uid, String tableName) {
        if (rule.getRuleSql() != null && !rule.getRuleSql().isEmpty()) {
            if ("all".equalsIgnoreCase(rule.getRuleSql())) return "";
            return rule.getRuleSql().replace("{uid}", uid);
        }

        if (rule.getDataScope() == DataPermissionRule.DataScopeType.个人) {
            return String.format("%s.create_by = '%s'", tableName, uid);
        }

        log.warn("未知数据范围类型，返回无权限条件");
        return "1=0";
    }

    /**
     * 更新BoundSQL中的原始语句
     */
    private void updateBoundSql(MappedStatement ms, BoundSql boundSql, String newSql) {
        var metaObject = ms.getConfiguration().newMetaObject(boundSql); // MetaObject metaObject = ms.getConfiguration().newMetaObject(boundSql);
        metaObject.setValue("sql", newSql);
    }

    /**
     * 合并多个数据范围规则到SQL语句
     */
    private String appendMultiDataScopeRules(String sql, Map<String, String> tableRules) throws JSQLParserException {
        Select select = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect ps = (PlainSelect) select.getSelectBody();

        // 处理主表条件
        processFromItem(ps.getFromItem(), tableRules, ps);

        // 处理JOIN表条件
        if (ps.getJoins() != null) {
            for (Join join : ps.getJoins()) {
                processJoin(join, tableRules);
            }
        }
        return select.toString();
    }

    /**
     * 处理主表条件追加
     */
    private void processFromItem(FromItem fromItem, Map<String, String> rules, PlainSelect ps) {
        if (fromItem instanceof Table table) {
            String alias = table.getAlias() != null ? table.getAlias().getName() : table.getName();
            String condition = rules.get(table.getName().toLowerCase());
            if (condition != null) {
                addWhereCondition(ps, condition.replace(table.getName() + ".", alias + ".").replace("{aliasName}",alias));
                rules.remove(table.getName().toLowerCase());
            }
        }
    }

    /**
     * 处理JOIN表条件追加
     */
    private void processJoin(Join join, Map<String, String> tableRules) {
        FromItem joinItem = join.getRightItem();
        if (joinItem instanceof Table) {
            Table joinTable = (Table) joinItem;
            String tableName = joinTable.getName().toLowerCase();
            String condition = tableRules.get(tableName);
            if (condition != null) {
                // 获取表别名（优先使用显式别名，否则默认表名）
                String alias = joinTable.getAlias() != null ?
                        joinTable.getAlias().getName() :
                        joinTable.getName();
                // 替换条件中的表名和别名变量
                String aliasedCondition = condition
                        .replace(tableName + ".", alias + ".")
                        .replace("{aliasName}", alias);
                addJoinCondition(join, aliasedCondition);
                tableRules.remove(tableName);
            }
        }
    }

    /**
     * 向WHERE子句追加条件
     */
    private void addWhereCondition(PlainSelect ps, String condition) {
        try {
            Expression newExpr = CCJSqlParserUtil.parseCondExpression(condition);
            ps.setWhere(ps.getWhere() == null ? newExpr : new AndExpression(ps.getWhere(), newExpr));
        } catch (JSQLParserException e) {
            log.error("WHERE条件解析失败: {}", condition, e);
        }
    }

    /**
     * 向JOIN子句追加条件（兼容新旧JSqlParser版本）
     */
    private void addJoinCondition(Join join, String condition) {
        try {
            Expression newExpr = CCJSqlParserUtil.parseCondExpression(condition);

            // 获取现有的ON条件列表（转换为可修改的ArrayList）
            List<Expression> onExpressions = new ArrayList<>(join.getOnExpressions());

            // 合并新旧条件
            if (onExpressions.isEmpty()) {
                onExpressions.add(newExpr);
            } else {
                // 将新条件与现有条件通过AND连接（注意处理多条件场景）
                Expression mergedExpr = onExpressions.stream()
                        .reduce(newExpr, AndExpression::new);
                onExpressions.clear();
                onExpressions.add(mergedExpr);
            }

            // 更新ON条件列表（需转换为Collection类型）
            join.setOnExpressions(onExpressions);
        } catch (JSQLParserException e) {
            log.error("JOIN条件解析失败: {}", condition, e);
        }
    }
}